package org.showminifier.servlet;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONException;
import org.json.JSONObject;
import org.json.XML;
import org.showminifier.core.Options;
import org.showminifier.web.PMF;
import org.showminifier.web.PageContent;
import org.showminifier.web.PagePerformance;
import org.showminifier.web.Util;
import org.showminifier.web.WebContent;

/**
*
* Show Minifier
* Author: Shihua Ma <beijing.josh@gmail.com>
* Copyright (c) 2010 Shihua Ma
* Licensed under the MIT license.
*
*/

@SuppressWarnings("serial")
public class DisplayServlet extends HttpServlet {
	
	private static final Logger log = Logger.getLogger(DisplayServlet.class.getName());
	
	public void doGet(HttpServletRequest req, HttpServletResponse resp)
	throws IOException{
		Long keyId = null;
		try{
			keyId = Long.parseLong(req.getParameter("key"));
		}catch( NumberFormatException e){
			resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
			return;
		}
		String type = req.getParameter("type");
		if(type == null || type.equals("")){
			type = "html";
		}
		PersistenceManager pm = PMF.get().getPersistenceManager();
		WebContent content = null;
		try{			
			content = pm.getObjectById(WebContent.class,keyId);
			if(content != null){
				resp.setHeader("Vary","Accept-Encoding");
				
				if(type.equalsIgnoreCase("html") || "source".equalsIgnoreCase(type)){
					toHtml(req,resp,content);
				}
				if(type.equalsIgnoreCase("json")){
					toJson(req,resp,content);
				}
				if(type.equalsIgnoreCase("xml")){
					toXml(req,resp,content);
				}
			}else{
				resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
				return;
			}
		}catch(JDOObjectNotFoundException e){
			resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
		}finally {
            pm.close();
        }
	}
	
	private void toHtml(HttpServletRequest req, HttpServletResponse resp, WebContent content) throws IOException{
		PageContent page = content.getPage();
		boolean source = false;
		if("source".equalsIgnoreCase(req.getParameter("type"))){
			source = true;
		}
		resp.setContentType(source ? "text/plain" : "text/html" );
		resp.setCharacterEncoding(content.getPage().getCharset());
		resp.setHeader("Cache-Control","private,max-age=0");
		String view = req.getParameter("view");
		String base = Util.getBaseHref(content.getUrl().getValue());
		String startJs ="<script>this['_startTime@Show:Minifier_'] = (new Date).getTime();</script>";
		String endJs ="<script>this['_performanceTime@Show:Minifier_'] = (new Date).getTime() - this['_startTime@Show:Minifier_'];this['_pageSendTime@Show:Minifier_']="+(new Date().getTime())+";</script>";
		String displayJs = "\r\n<!------------------Show Minifier insert script for display purpose------------------>\r\n<script src='" + req.getScheme() +
							"://" + req.getServerName() + (req.getServerPort() != 80 ? ":" + req.getServerPort() : "" ) + "/js/display.js'></script>";
		String value = null;		
		if(view != null && view.equalsIgnoreCase("o")){
			value = page.getOriginalContent().getValue();
		}else{
			value = page.getMinifiedContent().getValue();
		}
		if(value == null){
			resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
			return;
		}
		String performance = req.getParameter("performance");
		if(performance != null){
			value = Util.repalceBaseTag( value, base , startJs);
			value += endJs;
		}else if(!source){
			value = Util.repalceBaseTag( value, base , null);
		}
		if(!source){
			value += displayJs;
		}
		PrintWriter writer = resp.getWriter();
		writer.write(value);
		writer.flush();
		writer.close();
	}
	
	private void toJson(HttpServletRequest req, HttpServletResponse resp, WebContent content) throws IOException{
		String result = null;
		try {
			result = pageToJson(req,content).toString();
		} catch (JSONException e) {
			e.printStackTrace();
		}
		if(result == null){
			resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			return;
		}
		String callback = req.getParameter("callback");
		if(callback != null){
			result = callback + "(" + result + ");";
		}
		resp.setHeader("Content-Type","application/json");
		ServletOutputStream output = resp.getOutputStream();
		OutputStreamWriter write = new OutputStreamWriter(output,content.getPage().getCharset());
		write.write(result,0,result.length());
		write.flush();
		write.close();
	}
	
	private void toXml(HttpServletRequest req, HttpServletResponse resp, WebContent content) throws IOException{
		String xml = null;
		try {
			JSONObject json = pageToJson(req,content);
			xml = XML.toString(json);
		} catch (JSONException e) {
			e.printStackTrace();
		}
		if(xml != null){
			resp.setHeader("Content-Type","text/xml");
			ServletOutputStream output = resp.getOutputStream();
			output.print("<xml>" + xml + "</xml>");
			output.flush();
			output.close();
		}else{
			resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		}		
	}
	
	private JSONObject pageToJson(HttpServletRequest req,WebContent content) throws IOException, JSONException{
		PageContent page = content.getPage();
		JSONObject json = new JSONObject();
		JSONObject p = new JSONObject();
		JSONObject o = new JSONObject();
		DecimalFormat df = new DecimalFormat("###.#");
		df.setDecimalSeparatorAlwaysShown(false);
		DecimalFormat pf = new DecimalFormat("##.##%");
		pf.setDecimalSeparatorAlwaysShown(false);
		String view = req.getParameter("view");	
		
		json.put("key",req.getParameter("key"));
		json.put("server",req.getScheme()+"://"+req.getServerName()+ (req.getServerPort() != 80 ? ":" + req.getServerPort() : "" ));
		json.put("url",content.getUrl().getValue());
		if(view != null){
			if(view.equalsIgnoreCase("o")){
				json.put("originalContent",page.getOriginalContent().getValue());
			}else if (view.equalsIgnoreCase("o")){
				json.put("minifiedContent",page.getMinifiedContent().getValue());
			}else if (view.equalsIgnoreCase("all")){
				json.put("originalContent",page.getOriginalContent().getValue());
				json.put("minifiedContent",page.getMinifiedContent().getValue());
			}
		}
		
		p.put("chunked", page.isChunked());
		p.put("deflated", page.isDeflated());
		
		int originalLength = page.getOriginalContent().getValue().length();
		p.put("originalLength", df.format(originalLength / 1024D) + "KB");
		
		int minifiedLength = page.getMinifiedContent().getValue().length();
		p.put("minifiedLength", df.format(minifiedLength / 1024D) + "KB");
		
		int originalDeflatedLength = page.getOriginalDeflatedLength();
		p.put("originalDeflatedLength", df.format(originalDeflatedLength / 1024D) + "KB");
		
		int minifiedDeflatedLength = page.getMinifierDefaltedLength();
		p.put("minifiedDeflatedLength", df.format(minifiedDeflatedLength / 1024D) + "KB");
		
		int length = (page.isDeflated() ? originalDeflatedLength : originalLength);
		
		p.put("savings", df.format((originalLength - minifiedLength) / 1024D) + "KB");
		//p.put("deflatedSavings", df.format((length - minifiedDeflatedLength) / 1024D) + "KB");	
		p.put("deflatedSavings", df.format((originalDeflatedLength - minifiedDeflatedLength) / 1024D) + "KB");	
		
		p.put("minfiedPercent", pf.format((originalLength - minifiedLength) /(double) originalLength));
		
		//p.put("deflatedPercent",pf.format((length  - minifiedDeflatedLength)/ (double)length));
		p.put("deflatedPercent",pf.format((originalDeflatedLength  - minifiedDeflatedLength)/ (double)originalDeflatedLength));
		
		if(page.getDomDeep() > 0){
			p.put("domDeep", page.getDomDeep());
		}
		
		if(page.getDomNodeLength() > 0){
			p.put("domNodeLength", page.getDomNodeLength());
		}
		
		if(page.getDomAverageDeep() > 0){
			p.put("domAverageDeep", page.getDomAverageDeep());
		}
		
		p.put("requestHeader", page.getRequestHeaders());
		p.put("responseHeader", page.getResponseHeaders());
		
		json.put("page",p);
		
		Options option = content.getOptions();
		
		for(String key : Util.options.keySet()){
			Method init = null;
		    try {
		      init = Options.class.getDeclaredMethod("is"+Util.prettyName(key,true),null);
		      boolean value =(Boolean)init.invoke(option,null);
		      o.put(key,value);
		    } catch (Exception ex) {
		    	log.severe("Cound not get method[" + "is"+Util.prettyName(key,true) +"] at Options.class");
		    	throw new RuntimeException(ex);
		    }
		}
		/*
		o.put("collapse-boolean-attributes",opt.isCollapseBooleanAttributes());
		o.put("collapse-whitespace",opt.isCollapseWhitespace());
		o.put("remove-attribute-quotes",opt.isRemoveAttributeQuotes());
		o.put("remove-comments-from-cdata",opt.isRemoveCdataSectionsFromCdata());
		o.put("remove-comments",opt.isRemoveComments());
		o.put("remove-cdata-sections-from-cdata",opt.isRemoveCommentsFromCdata());
		o.put("remove-empty-attributes",opt.isRemoveEmptyAttributes());
		o.put("remove-optional-tags",opt.isRemoveOptionalTags());
		o.put("remove-redundant-attributes",opt.isRemoveRedundantAttributes());
		o.put("remove-url-scheme",opt.isRemoveUrlScheme());
		o.put("use-short-doctype",opt.isUseShortDoctype());
		o.put("remove-script-type-attributes",opt.isRemoveScriptTypeAttributes());
		o.put("remove-style-type-attributes",opt.isRemoveStyleTypeAttributes());
		o.put("remove-empty-elements",opt.isRemoveEmptyElements());
		*/
		json.put("options",o);
		
		
		return json;
	}
	
	private void performanceToJson(HttpServletRequest req, HttpServletResponse resp, WebContent content) throws IOException{
		List<PagePerformance> performances = content.getPerformance();
		resp.setStatus(HttpServletResponse.SC_NOT_IMPLEMENTED);
	}
	
	
}
